/*
<license>
This software is distributed under the Simplified BSD License:

Copyright (c) 2008, Chris Venter <chris.venter@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
    	this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, 
    	this list of conditions and the following disclaimer in the documentation 
    	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE.
</license>
*/
#ifndef GENERIK_TRACING_H
#define GENERIK_TRACING_H

#include "macros.h"

/** \file tracing.h

This header defines a couple of macros and a framework for
printf-style debugging. If no tracing type is defined, the default
is to output nothing: all the macros evaluate as empty.
The two types of tracing are GENERIK_TRACING and GENERIK_LOGGING

\todo
This file needs some unit-testing itself.
First we need to run a fully-traced GENERIK_TRACING test, and check whether output is produced
everywhere.
Then we need to run a GENERIK_LOGGING test - only should be traced
Then, we run a clean test, with full optimization -O3, and only errors should be output,
and then only on std::cerr?
*/

// #####################
// ## GENERIK_TRACING ##
// #####################
/**
\def GENERIK_TRACING
Enables deep tracing. In every scope where a
GENERIK_TRACER is placed, a tracing statement will be output.
To output general messages, use GENERIK_TRACE. 
Logging and error-reporting is available through GENERIK_LOG and
GENERIK_ERROR, respectively.

This definition must be placed either directly in the code or (preferably)
passed directly to the compiler (ie. as -DGENERIK_TRACING for gcc).
*/

// ########################
// ## GENERIK_TRACE_TIME ##
// ########################
/**
\def GENERIK_TRACE_TIME
If this is defined, each GENERIK_TRACER call will also trace the amount of real time
spent inside it.
*/

// ########################
// ## GENERIK_TRACE_VMEM ##
// ########################
/**
\def GENERIK_TRACE_VMEM
If this is defined, the amount of virtual memory used (by the application at the point
of tracing) will also be output.

\todo Not sure if this still works...?
*/

// ###########################
// ## GENERIK_TRACER_GLOBAL ##
// ###########################
/**
\def GENERIK_TRACER_GLOBAL
Instantiates the global tracer, which outputs some prelimenary debug info,
and also outputs a message when it leaves scope. This macro should be called
by the main source file, before any other structure is declared, so that the 
messages it produces "wraps" all other tracing output.
This is not a strict requirement.
*/
#ifdef GENERIK_TRACING
#define GENERIK_TRACER_GLOBAL \
	generik::tracing::global generik_tracing_global;
#else
#define GENERIK_TRACER_GLOBAL
#endif

// ####################
// ## GENERIK_TRACER ##
// ####################
/**
\def GENERIK_TRACER
Instantiates a local tracer, which automatically logs the name of the function
where it resides. Upon destruction, the tracer logs an exit message.
*/
#ifdef GENERIK_TRACING
#define GENERIK_TRACER \
	generik::tracing::tracer generik_tracing_tracer(__PRETTY_FUNCTION__)
#else
#define GENERIK_TRACER
#endif

// ########################
// ## REM_GENERIK_TRACER ##
// ########################
/**
\def REM_GENERIK_TRACER
Helper macro. Use this to silence the existing tracers in the current scope, without
having to remove them.
*/
#ifdef GENERIK_TRACING
#define REM_GENERIK_TRACER \
	generik::tracing::tracer generik_tracing_tracer
#else
#define REM_GENERIK_TRACER
#endif

// ###################
// ## GENERIK_TRACE ##
// ###################
/**
\def GENERIK_TRACE(message)
Outputs the given message. The message may be in stream-format, ie:
\code GENERIK_TRACE("hello "<<world); \endcode
*/
#ifdef GENERIK_TRACING
#define GENERIK_TRACE(message) \
	{ \
		std::stringstream buf; \
		buf<<message; \
		generik_tracing_tracer.trace(buf.str()); \
	}
#else
#define GENERIK_TRACE(message)
#endif

// ####################
// ## GENERIK_INLINE ##
// ####################
/**
\def GENERIK_INLINE(message)
Outputs the given message. The message may be in stream-format, ie:
\code GENERIK_TRACE("hello "<<world); \endcode
This call does not depend on a wrapping GENERIK_TRACER being present.
It serves to "inline" the message.
Use it (sparsely) to document functions which do not need the cumbersome
enter and exit messages, for example locking and guarding functions.
*/
#ifdef GENERIK_TRACING
#define GENERIK_INLINE(message) \
	{ \
		std::stringstream buf; \
		buf<<message; \
		generik::tracing::output(buf.str()); \
	}
#else
#define GENERIK_INLINE(message)
#endif

// #########################
// ## GENERIK_TRACE_VALUE ##
// #########################
/**
\def GENERIK_TRACE_VALUE(instance)
Shortcut which traces the value of the instance and its name.
For example,
	std::string hello = "world";
	GENERIK_TRACE_VALUE(hello);
results in something like
	hello == [world]
*/
#ifdef GENERIK_TRACING
#define GENERIK_TRACE_VALUE(instance) \
	{ \
		GENERIK_TRACE(#instance << " == [" << instance << "]"); \
	}
#else
#define GENERIK_TRACE_VALUE(instance)
#endif

// ###################
// ## GENERIK_ERROR ##
// ###################
/** 
\def GENERIK_ERROR(message)
Logs the given message as an error.The format of the message is the same
as for GENERIK_TRACE.
It logs the message using both the GENERIK_TRACER paradigm, as well as dumping
the message directly to stderr.
*/
#ifdef GENERIK_TRACING
#define GENERIK_ERROR(message) \
	{ \
		std::stringstream buf; \
		buf<<message<<" in "<<__PRETTY_FUNCTION__<<":"GENERIK_FILE_LINE"\n"; \
		generik_tracing_tracer.error(buf.str()); \
	} \
	{ \
		std::stringstream buf; \
		buf<<message<<"\n"; \
		std::cerr<<buf.str()<<std::flush; \
	}
#else
#define GENERIK_ERROR(message) \
	{ \
		std::stringstream buf; \
		buf<<message<<"\n"; \
		std::cerr<<buf.str()<<std::flush; \
	}
#endif

// ###########################
// ## GENERIK_TRACER_ACTIVE ##
// ###########################
/** 
\def GENERIK_TRACER_ACTIVE
This returns the boolean _active value of the current generik_tracing_tracer.
If tracing is disabled, this is always false.
It can be uses to test whether a block of code should be run or not, depending
on the state of the tracer, ie. GENERIK_TRACER or REM_GENERIK_TRACER.
Use with caution, as this code will *never* execute in RELEASE builds.
*/
#ifdef GENERIK_TRACING
#define GENERIK_TRACER_ACTIVE (generik_tracing_tracer.active())
#else
#define GENERIK_TRACER_ACTIVE (false)
#endif

// #############
// ## Classes ##
// #############

#include <iostream>
#include <fstream>
#include <sstream>

#include <stdlib.h>

//#define GENERIK_INIT_TRACING
#ifdef GENERIK_INIT_TRACING
#define GENERIK_INIT_TRACE(STREAM) std::clog<<STREAM<<std::endl;
#else
#define GENERIK_INIT_TRACE(STREAM)
#endif

/*
Add a GENERIK_TRACE_LEVEL environment variable.
Add a generik::tracing::init::_level variable.
Each time we enter a tracer, _level is incremented.
Each time we leave, it is decremented.
This is hard with threaded programs - keep it as a per-thread key?
So init would store the key(s), and take care of them???

If GENERIK_TRACE_LEVEL is set to a value greater than 0, then
trace only until that level is reached.
If it is set to 0, do not trace at all.
If less than zero (ie. -1) trace everything.

Have a GENERIK_TRACE_REMS variable, if it is set trace
even REM_GENERIK_TRACERs.

Have a GENERIK_BLACKLIST list variable. Values in this list
are checked against the compile time GENERIK_COMPONENT variable, it is in the 
list, it is NOT traced. This makes it useful to quiet down noisy
libraries, for example?
Therefore GENERIK_COMPONENT has to be separately specified by each module.

Move all the implementation of the init and tracers to the tracing.cc file.
This puts us in control of which optimisations are used to compile these
structures - Also this means that we can replace an activley tracing executable
with a non-tracing executable by simply replacing the library.
*/

namespace generik
{
	namespace tracing
	{
		
		std::ostream* get_stream();
		std::ostream* set_stream(std::ostream* stream_in);
		std::ostream* set_stream(const std::string& filename);
		
		void output(const std::string& message);
		
		struct init
		{
			init();
			
			std::ostream* get_stream();
			
			std::ostream* set_stream(std::ostream* stream_in);
			void set_stream(const std::string& filename);
			
		private:
			
			std::ostream* _stream;
			
		};
		
	}
}

#include <cxxabi.h>
#include <pthread.h>
#include <unistd.h>

namespace generik
{
	
	/** This namespace collects the tracer object.
	*/
	namespace tracing
	{
		
		/** Scoped logging object.
		When an instance of this class is created, it makes a start log-entry,
		and when destroyed makes a stop log-entry.
		If it is the first declaration in a function body, it will nicely delimit the
		borders of the function in the log output, which is the whole idea.
		\warning Do not use this directly in the code. Please use the Macros.
		*/
		struct tracer
		{
			
			tracer();
			tracer(const std::string& function_in);
			
			virtual ~tracer();
			
			/**
			These are the publicly available trace functions
			*/
			///{

			void trace(const std::string& message_in);
			void log(const std::string& message_in);
			void error(const std::string& message_in);
			void assert_(const std::string& evalstr_in, bool);
			void print(const std::string& message_in);
			bool active() const;
			
			///}
			
		private:
			
			bool _active;
			std::string _function;
			clock_t _start0;
		
			void msg(char pre, const std::string& message_in);
		};
		
		struct global
		: tracer
		{
			global()
			: tracer("process")
			{}
		};
	
	}

}

#endif


